import datetime
import pandas as pd

from coin.exchange.kr_rest.product.product_impl import generate_product_from_str
from coin.proto.coin_request_pb2 import AccountRequestProto
from coin.proto.account_control_pb2 import (
    FeeRateResponse,
    FeeRate,
)
from xunkemgmt.support.accounting.logic.query_request import AccountRequest
from coin.support.accounting.logic.account_status_util import query_account_fills
from coin.exchange.okex_futures.kr_rest.futures_product import OkexFuturesProduct
from coin.exchange.huobi_futures.kr_rest.futures_product import HuobiFuturesProduct


def account_request_to_control_target(account_request: AccountRequest, product=None):
  control_target = AccountRequestProto(market_type=account_request.market_type,
                                       exchange=account_request.exchange,
                                       owner=account_request.owner)
  if account_request.api_version is not None:
    control_target.api_version = account_request.api_version
  if product is not None:
    if isinstance(product, str):
      control_target.product = product
    else:
      control_target.product = product.symbol
  return control_target


def control_target_to_account_request(target: AccountRequestProto):
  if target.exchange == 'Okex':
    if 'swap' in target.owner:
      api_version = 'v3_swap'
    else:
      api_version = 'v3'
  else:
    api_version = None
  account_request = AccountRequest(market_type=target.market_type,
                                   exchange=target.exchange,
                                   api_version=api_version,
                                   owner=target.owner if target.HasField('owner') else None,
                                   accounting_currency=None)
  return account_request


def generate_product_from_control_target(control_target):
  assert control_target.HasField('product')
  exchange = control_target.exchange
  api_version = control_target.api_version if control_target.HasField('api_version') else None
  symbol = control_target.product
  product = generate_product_from_str(exchange, api_version, symbol)
  return product


def _calculate_fee_rate_from_fills(fills, product, quote_price=1):
  fill_bundle = fills[product].msg
  if product.exchange in ['Okex']:
    if product.product_type == 'Futures':
      if 'USDT' in product.symbol:
        _calc_func = lambda fill: fill.fee / (fill.qty * fill.contract_val * fill.price)
      else:
        _calc_func = lambda fill: fill.fee * fill.price / (fill.qty * fill.contract_val)
    elif product.product_type == 'CurrencyPair':
      _calc_func = lambda fill: fill.fee/fill.qty/fill.price/quote_price \
                   if fill.fee_currency in ['USDT', 'OKDK'] else fill.fee/fill.qty
    else:
      raise NotImplementedError(product)

    fee_rates = []
    fee_rate_value_list = []
    for fill in fill_bundle.each_fill:
      if not fill.HasField('fee'):
        continue
      fee_rate = _calc_func(fill)
      fee_rate = round(fee_rate, 5)
      fee_rate_value_list.append([fee_rate, fill.qty, fill.fill_type])
    fee_rate_value_df = pd.DataFrame(fee_rate_value_list)
    fee_rate_value_df.columns = ['fee_rate', 'qty', 'type']
    for fill_type in set(fee_rate_value_df['type'].tolist()):
      fee_rate_temp = fee_rate_value_df[fee_rate_value_df['type'] == fill_type]
      if len(fee_rate_temp) > 0:
        fee_rates.append(FeeRate(fee_rate=fee_rate_temp['fee_rate'].iloc[0], type=fill_type))
    return fee_rates
  else:
    raise NotImplementedError(product)


def calculate_fee_rate(fee_rate_request, quote_usdt=None):
  request = control_target_to_account_request(fee_rate_request.target)
  current_datetime = datetime.datetime.now()
  if request.market_type == 'Futures' and request.exchange == 'Okex':
    product = OkexFuturesProduct.FromStr(fee_rate_request.symbol, current_datetime)
  elif request.market_type == 'Futures' and request.exchange == 'Huobi':
    product = HuobiFuturesProduct.FromStr(fee_rate_request.symbol, current_datetime)
  else:
    product = generate_product_from_str(request.exchange, None, fee_rate_request.symbol)
  fills = query_account_fills(request, [product])
  if quote_usdt:
    quote_price = quote_usdt.get(product.quote.currency, 1)
  else:
    quote_price = 1
  fee_rates = _calculate_fee_rate_from_fills(fills, product, quote_price)
  return FeeRateResponse(target=fee_rate_request.target,
                         symbol=fee_rate_request.symbol,
                         fee_rates=fee_rates)
